package pers.cz.javacvdemo.redis;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.convert.Converters;
import org.springframework.data.redis.core.*;

import java.util.*;
import java.util.Map.Entry;
import java.util.stream.Collectors;

@Slf4j
public class HashOp extends Operation {

	private final HashOperations<String, String, String> HASH;

	public HashOp(StringRedisTemplate redisTemplate) {
		this.HASH = redisTemplate.opsForHash();
	}

	public Long hDel(String key, String... fields) {
		try {
			return HASH.delete(key, Arrays.stream(fields).toArray());
		} catch (Exception e) {
			log.warn("hDel exception, key={},fields={}", key, fields, e);
		}
		return null;
	}

	public boolean hExists(String key, String field) {
		try {
			return HASH.hasKey(key, field);
		} catch (Exception e) {
			log.warn("hExists exception, key={},field={}", key, field, e);
		}
		return false;
	}

	public <T> T hGet(String key, String field, Class<T> clz) {
		try {
			return deserialize(HASH.get(key, field), clz);
		} catch (Exception e) {
			log.warn("hGet exception, key={},field={}", key, field, e);
		}
		return null;
	}

	public <T> Map<String, T> hGetAll(String key, Class<T> clz) {
		try {
			Map<String, String> entries = HASH.entries(key);
			Map<String, T> map = new LinkedHashMap<>(entries.size());
			for (Entry<String, String> entry : entries.entrySet()) {
				map.put(entry.getKey(), deserialize(entry.getValue(), clz));
			}
			return map;
		} catch (Exception e) {
			log.warn("hGetAll exception, key={}", key, e);
		}
		return null;
	}

	public Map<String, String> hGetAll(String key) {
		try {
			return HASH.entries(key);
		} catch (Exception e) {
			log.warn("hGetAll exception, key={}", key, e);
		}
		return null;
	}


	public long hIncrBy(String key, String field, long delta) {
		try {
			return HASH.increment(key, field, delta);
		} catch (Exception e) {
			log.warn("hIncrBy exception, key={},field={}", key, field, e);
		}
		return 0L;
	}

	public double hIncrByFloat(String key, String field, double delta) {
		try {
			return HASH.increment(key, field, delta);
		} catch (Exception e) {
			log.warn("hIncrByFloat exception, key={},field={}", key, field, e);
		}
		return 0L;
	}

	public Set<String> hKeys(String key) {
		try {
			return HASH.keys(key);
		} catch (Exception e) {
			log.warn("hKeys exception, key={}", key, e);
		}
		return null;
	}

	public Long hLen(String key) {
		try {
			return HASH.size(key);
		} catch (Exception e) {
			log.warn("hLen exception, key={}", key, e);
		}
		return null;
	}

	public <T> List<T> hmGet(String key, Collection<String> fields, Class<T> clz) {
		try {
			List<String> list = HASH.multiGet(key, fields);
			return list.stream().map(str -> deserialize(str, clz)).collect(Collectors.toList());
		} catch (Exception e) {
			log.warn("hmGet exception, key={},fields={}", key, fields, e);
		}
		return null;
	}

	public List<String> hmGet(String key, Collection<String> fields) {
		try {
			return HASH.multiGet(key, fields);
		} catch (Exception e) {
			log.warn("hmGet exception, key={},fields={}", key, fields, e);
		}
		return null;
	}

	public <T> void hmSet(String key, Map<String, T> entries) {
		try {
			Map<String, String> hashes = new LinkedHashMap<>(entries.size());
			for (Entry<String, T> entry : entries.entrySet()) {
				hashes.put(entry.getKey(), serialize(entry.getValue()));
			}
			HASH.putAll(key, hashes);
		} catch (Exception e) {
			log.warn("hmSet exception, key={},entries={}", key, entries, e);
		}
	}

	public void hSet(String key, String field, Object value) {
		try {
			HASH.put(key, field, serialize(value));
		} catch (Exception e) {
			log.warn("hSet exception, key={},field={}", key, field, e);
		}
	}

	public void hSetNX(String key, String field, Object value) {
		try {
			HASH.putIfAbsent(key, field, serialize(value));
		} catch (Exception e) {
			log.warn("hSetNX exception, key={},field={}", key, field, e);
		}
	}

	public <T> List<T> hVals(String key, Class<T> clz) {
		try {
			List<String> values = HASH.values(key);
			return values.stream().map(str -> deserialize(str, clz)).collect(Collectors.toList());
		} catch (Exception e) {
			log.warn("hVals exception, key={}", key, e);
		}
		return null;
	}

	public List<String> hVals(String key) {
		try {
			return HASH.values(key);
		} catch (Exception e) {
			log.warn("hVals exception, key={}", key, e);
		}
		return null;
	}

	public <T> Cursor<Entry<String, T>> scan(String key, ScanOptions scanOptions, Class<T> clz) {
		try {
			return new ConvertingCursor<>(HASH.scan(key, scanOptions),
					(source) -> Converters.entryOf(source.getKey(), this.deserialize(source.getValue(), clz)));
		} catch (Exception e) {
			log.warn("scan exception, key={},scanOptions={}", key, scanOptions, e);
		}
		return null;
	}

	public Cursor<Entry<String, String>> scan(String key, ScanOptions scanOptions) {
		try {
			return new ConvertingCursor<>(HASH.scan(key, scanOptions),
					(source) -> Converters.entryOf(source.getKey(), source.getValue()));
		} catch (Exception e) {
			log.warn("scan exception, key={},scanOptions={}", key, scanOptions, e);
		}
		return null;
	}

	public String hRandField(String key) {
		try {
			return HASH.randomKey(key);
		} catch (Exception e) {
			log.warn("hRandField exception, key={}", key, e);
		}
		return null;
	}

	public List<String> hRandField(String key, long count) {
		try {
			return HASH.randomKeys(key, count);
		} catch (Exception e) {
			log.warn("hRandField exception, key={},count={}", key, count, e);
		}
		return null;
	}

	public <T> Entry<String, T> hRandFieldWithValue(String key, Class<T> clz) {
		try {
			Entry<String, String> entry = HASH.randomEntry(key);
			if (entry == null) {
				return null;
			}
			return Converters.entryOf(entry.getKey(), deserialize(entry.getValue(), clz));
		} catch (Exception e) {
			log.warn("hRandFieldWithValues exception, key={}", key, e);
		}
		return null;
	}

	public <T> Map<String, T> hRandFieldWithValues(String key, long count, Class<T> clz) {
		try {
			Map<String, String> entries = HASH.randomEntries(key, count);
			if (entries == null) {
				return null;
			}
			Map<String, T> result = new LinkedHashMap<>(entries.size());
			entries.forEach((k, v) -> {
				result.put(k, deserialize(v, clz));
			});
			return result;
		} catch (Exception e) {
			log.warn("hRandFieldWithValues exception, key={}，count={}", key, count, e);
		}
		return null;
	}

	public Map<String, String> hRandFieldWithValues(String key, long count) {
		try {
			return HASH.randomEntries(key, count);
		} catch (Exception e) {
			log.warn("hRandFieldWithValues exception, key={}，count={}", key, count, e);
		}
		return null;
	}

	public Long hStrLen(String key, String hashKey) {
		try {
			return HASH.lengthOfValue(key, hashKey);
		} catch (Exception e) {
			log.warn("hStrLen exception, key={}，hashKey={}", key, hashKey, e);
		}
		return null;
	}
}
